﻿// <copyright file="DebugModeProcessor.cs" company="TenneySoftware">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Robert Tenney</author>
// <email>rob10e@yahoo.com</email>
// <date>10/6/2009</date>
// <summary>Processes the debug modes specific to the module.</summary>

// License:

// Product: Engine.csproj
// Author: Robert N. Tenney
// Copyright: 10/6/2009

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation version 2
// of the License.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// This license covers all source code, binaries, and other materials related to this project
// unless specifically stated otherwise.

using System;
using System.Collections.Generic;

namespace TenneySoftware.Engine.Debug {
    /// <summary>
    /// Processes the debug modes specific to the module.
    /// </summary>
    public class DebugModeProcessor {
        /// <summary>
        /// Debug modes and their states.
        /// </summary>
        private DebugModeCollection debugModes;

        /// <summary>
        /// List with the brief help text.
        /// </summary>
        private List<string> sparseHelp;

        /// <summary>
        /// List with the full help text.
        /// </summary>
        private List<string> fullHelp;

        /// <summary>
        /// The module descriptor.
        /// </summary>
        private string module;

        /// <summary>
        /// Initializes a new instance of the DebugModeProcessor class.
        /// </summary>
        /// <param name="debugModes">A string array defining the debug modes.</param>
        /// <param name="module">The module description.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Lowered is preferred.")]
        public DebugModeProcessor(DebugMode[] debugModes, string module) {
            this.debugModes = new DebugModeCollection();

            // Load the modes into the list.
            foreach (DebugMode mode in debugModes) {
                this.debugModes.Add(mode);
            }

            // Set up help
            this.sparseHelp = new List<string>();
            foreach (DebugMode mode in debugModes) {
                this.sparseHelp.Add("\t" + mode.Name + " - " + mode.Help + (mode.DebugGroup != 0 ? " Group #: " + mode.DebugGroup : string.Empty));
            }
            
            this.sparseHelp.Add("\n\tAll - Debug mode modification affects all modes at once. (All on or all off).");
            this.sparseHelp.Add("\tNone - Turns off all debug modes. Equal to 'all off'.");

            this.fullHelp = new List<string>();
            this.fullHelp.Add("Usage: [debug] [[command] [on|off]] | debug [code]");
            this.fullHelp.Add("Prefix with '" + module.ToLowerInvariant() + ".' to target " + module.ToLowerInvariant() + " only commands.");
            this.fullHelp.Add("\nUse of 'debug' is optional.");
            this.fullHelp.Add("'Debug' with a value will turn on all of the modes in that group.");
            this.fullHelp.Add("'Debug' alone will display status of all available modes.");
            this.fullHelp.Add("\nCommands:");
            this.fullHelp.AddRange(this.sparseHelp);
            
            this.module = module;
        }

        /// <summary>
        /// Gets the DebugModes defined.
        /// </summary>
        /// <value>
        /// The DebugModes defined.
        /// </value>
        public DebugModeCollection DebugModes {
            get {
                return this.debugModes;
            }
        }

        /// <summary>
        /// Processes the command string sent by the DebugConsole.
        /// </summary>
        /// <param name="command">Command string to process.</param>
        /// <returns>True if the command was handled.</returns>
        public bool ConsoleCommand(string command) {
            bool result = false;
            bool sparseHelp = false;
            bool isDebug = false;
            string debugCommand = string.Empty;
            int debugCode = 0;
            bool turnOn = false;
            bool toggle = true;
            
            // Check to see if we are using the abbreviated help.
            if (command.ToUpperInvariant() == "SPARSEHELP") {
                sparseHelp = true;
            }

            // Replace the '?' with the word 'help'.
            command = command.Replace("?", "help");
            string[] parsed = command.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (sparseHelp) {
                DebugConsole.Console.OutputBuffer.Add("Module: " + this.module + ":");
            }
            
            // Check to see if we are using debug groups.
            if (string.Compare(parsed[0], "debug", StringComparison.OrdinalIgnoreCase) == 0) {
                if (parsed.GetLength(0) > 1) {
                    int grp = 0;
                    
                    // Parse the numerical value after the 'debug' command.
                    if (Int32.TryParse(parsed[1], out grp)) {
                        for (int iter = 0; iter < this.debugModes.Count; iter++) {
                            if (this.debugModes[iter].DebugGroup == grp) {
                                this.debugModes[iter].Value = true;
                            } else {
                                this.debugModes[iter].Value = false;
                            }
                        }
                        
                        return true;
                    }
                }
            }

            // Iterate through each word in the command string and see if there is a match.
            foreach (string com in parsed) {
                switch (com.ToUpperInvariant()) {
                    case "HELP":
                        // Display full help.
                        foreach (string message in this.fullHelp) {
                            DebugConsole.Console.OutputBuffer.Add(message);
                        }
                        
                        result = true;
                        break;
                    case "SPARSEHELP":
                        // Display abbreviated help.
                        foreach (string message in this.sparseHelp) {
                            DebugConsole.Console.OutputBuffer.Add(message);
                        }
                        
                        result = true;
                        break;
                    case "DEBUG":
                        // 'Debug' is called, but not for a group.
                        isDebug = true;
                        break;
                    case "ON":
                        // Turn on the command.
                        turnOn = true;
                        toggle = false;
                        break;
                    case "OFF":
                        // Turn off the command.
                        turnOn = false;
                        toggle = false;
                        break;
                    case "NONE":
                    case "ALL":
                        // Sets all of the modes either true or false depending on what was called.
                        this.debugModes.SetAll(string.Compare(com, "ALL", StringComparison.OrdinalIgnoreCase) == 0);
                        return true;
                    default:
                        // Anything else is assumed to be a debugmode command.
                        if (String.IsNullOrEmpty(debugCommand)) {
                            if (this.debugModes.ContainsName(com)) {
                                debugCommand = com;
                                isDebug = true;
                            }
                        }

                        break;
                }
            }

            // Check to see if we are using a debug mode.
            if (isDebug) {
                // Make sure the command is not empty.
                if (!String.IsNullOrEmpty(debugCommand)) {
                    // Check to see if we are toggling a state.
                    if (toggle) {
                        // Check the current state and set it to the opposite.
                        if (this.debugModes.GetValue(debugCommand)) {
                            turnOn = false;
                        } else {
                            turnOn = true;
                        }
                    }
                    
                    // If not toggling, check to see if we are turning on.
                    if (turnOn) {
                        // Turn the mode on.
                        this.debugModes.SetValue(debugCommand, true);
                    } else {
                        // Turn the mode off.
                        this.debugModes.SetValue(debugCommand, false);
                    }

                    // Tell the user what was changed.
                    DebugConsole.Console.OutputBuffer.Add(this.module + ": Debug mode: '" + debugCommand + "' has been turned " + (turnOn ? "on" : "off") + ".");
                    result = true;
                }

                // 'Debug' was called without any suffix or group number, so display the states of the modes.
                if (debugCode == 0 && String.IsNullOrEmpty(debugCommand)) {
                    DebugConsole.Console.OutputBuffer.Add(this.module + ": Debug modes states:");
                    foreach (DebugMode modeState in this.debugModes) {
                        DebugConsole.Console.OutputBuffer.Add("   " + modeState.Name + ": " + (modeState.Value ? "On" : "Off"));
                    }

                    result = true;
                }
            }

            return result;
        }

        /// <summary>
        /// Updates the dynamic messages. The dynamic messages are the debug messages which are persistant and need
        /// regular updating.
        /// </summary>
        /// <param name="messages">A collection with the messages.</param>
        public void UpdateMessages(DebugDynamicMessageCollection messages) {
            foreach (DebugDynamicMessage message in messages) {
                if (this.debugModes.GetValue(message.Name)) {
                    if (DebugBuffer.Messages.Contains(message.Name)) {
                        int ind = DebugBuffer.Messages.FindIndex(message.Name);
                        DebugBuffer.Messages[ind].Message = message.Message;
                    } else {
                        DebugBuffer.Messages.Add(new DebugBuffer(message.Name, message.Message, message.Priority, message.Position));
                        DebugBuffer.Messages.Sort();
                    }
                } else if (DebugBuffer.Messages.Contains(message.Name)) {
                    DebugBuffer.Messages.Remove(message.Name);
                    DebugBuffer.Messages.Sort();
                }
            }
        }
    }
}